package ad_exercise_4.model;

import java.util.ArrayList;

/**
 * Wrapper around a decompression table that is built while decompressing a file
 * 
 * @author Karen Berntsen (s0815799)
 * @author Erik Stens (s0615374)
 */
public class LZWDecompressor {
	private class DecompressionCode {
		public int	prefix	= -1, lastChar = -1;
		
		public DecompressionCode(int prefix, int lastChar) {
			this.prefix = prefix;
			this.lastChar = lastChar;
		}
	}
	
	private static final int	NUM_INITIAL_CODES	= 256, MAX_CODE = 4096;
	
	private DecompressionCode[]	decompressionTable;
	private int					nextCode			= NUM_INITIAL_CODES;
	private int					lastCode;
	private boolean				justStarted			= true;
	
	public LZWDecompressor(int initialLastCode) {
		decompressionTable = buildDecompressionTable();
		lastCode = initialLastCode;
	}
	
	/**
	 * Fills the decompression table with the 256 single ASCII characters
	 * 
	 * @return the initial decompression table
	 */
	private DecompressionCode[] buildDecompressionTable() {
		DecompressionCode[] decompressionTable = new DecompressionCode[4096];
		for (int i = 0; i < 256; i++) {
			decompressionTable[i] = new DecompressionCode(-1, i);
		}
		return decompressionTable;
	}
	
	/**
	 * Recursive function for retrieving the first character of a prefix in the decompression table
	 * 
	 * If a prefix is not set (standard -1) then the lastChar of the DecompressionCode is returned
	 * 
	 * @param code
	 * @return the first character of the prefix described by the argument 'code'
	 */
	private int getFirstChar(int code) {
		if (code < NUM_INITIAL_CODES) {
			return code;
		}
		
		int prefix = decompressionTable[code].prefix;
		
		if (prefix == -1) {
			return decompressionTable[code].lastChar;
		}
		
		return getFirstChar(prefix);
	}
	
	/**
	 * Using the getFromDecompressionTableRecursive() function, recursively finds the list of characters describing the prefix that the argument 'code' represents
	 * 
	 * @param code
	 * @return ArrayList<Integer> describing the prefix
	 */
	private ArrayList<Integer> getFromDecompressionTable(int code) {
		return getFromDecompressionTableRecursive(new ArrayList<Integer>(), code);
	}
	
	private ArrayList<Integer> getFromDecompressionTableRecursive(ArrayList<Integer> intList, int code) {
		if (code < 256) {
			intList.add(decompressionTable[code].lastChar);
			return intList;
		} else {
			intList = getFromDecompressionTableRecursive(intList, decompressionTable[code].prefix);
			intList.add(decompressionTable[code].lastChar);
			return intList;
		}
	}
	
	/**
	 * Updates the decompression table and returns the decompressed code as an ArrayList<Integer> of characters
	 * 
	 * @param newCode
	 *            the next code to be decompressed
	 * @return decompressed code as an ArrayList<Integer> of characters
	 */
	public ArrayList<Integer> nextData(int newCode) {
		if (nextCode < MAX_CODE && !justStarted) {
			decompressionTable[nextCode] = decompressionTable[lastCode];
			decompressionTable[nextCode] = new DecompressionCode(lastCode, getFirstChar(newCode));
			nextCode++;
			lastCode = newCode;
		}
		justStarted = false;
		return getFromDecompressionTable(newCode);
	}
}
